package org.luxor.commons.workflow.service.impl;

import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.ExecutionEntityImpl;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.apache.commons.lang.StringUtils;
import org.luxor.commons.workflow.component.cache.CustomDeploymentCache;
import org.luxor.commons.workflow.config.properties.WorkflowProperties;
import org.luxor.commons.workflow.constant.ExtensionVariableConst;
import org.luxor.commons.workflow.constant.TransientVariableConst;
import org.luxor.commons.workflow.constant.VariableConst;
import org.luxor.commons.workflow.entity.ProcessInstanceQuery;
import org.luxor.commons.workflow.entity.StartProcess;
import org.luxor.commons.workflow.service.IRuntimeCoreService;
import org.luxor.commons.workflow.utils.ProcessUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 流程执行管理
 *
 * @author Mr.yan  @date 2021/10/16
 */
@Service
public class RuntimeCoreServiceImpl implements IRuntimeCoreService {

    @Resource
    private RuntimeService runtimeService;
    @Resource
    private WorkflowProperties workflowProperties;
    @Resource
    private RepositoryService repositoryService;
    @Resource
    private HistoryService historyService;
    @Resource
    private TaskService taskService;
    @Resource
    private CustomDeploymentCache deploymentCache;

    /**
     * 发起流程
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ProcessInstance start(StartProcess start) {
        // 设置发起人
        Authentication.setAuthenticatedUserId(start.getStartUserId());

        // 流程变量
        Map<String, Object> variables = ProcessUtils.getProcessExtensionProperties(start.getProcessDefinitionId());
        variables.put(VariableConst.PROCESS_NAME, start.getName());
        variables.put(VariableConst.BUSINESS_KEY, start.getBusinessKey());
        variables.put(VariableConst.START_USER_ID, start.getStartUserId());
        variables.putAll(start.getVariables());

        // 发起流程
        ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder()
                .processDefinitionId(start.getProcessDefinitionId())
                .name(start.getName())
                .businessKey(start.getBusinessKey())
                .tenantId(start.getTenantId())
                .variables(variables)
                .start();

        // 新增[运行实例流程定义]
        deploymentCache.copyToRuProcessDefinition(start.getProcessDefinitionId(), processInstance.getId());

        // 跳过第一个节点？
        Object firstNodeSkip = ((ExecutionEntityImpl) processInstance).getVariable(ExtensionVariableConst.FIRST_NODE_SKIP);
        if (firstNodeSkip == null || Boolean.valueOf(firstNodeSkip.toString())) {
            List<TaskEntity> firstTaskList = ((ExecutionEntity) processInstance).getExecutions().get(0).getTasks();
            for (TaskEntity firstTask : firstTaskList) {

                // 发起人自动签收任务
                taskService.claim(firstTask.getId(), processInstance.getStartUserId());

                // 添加操作记录
                Authentication.setAuthenticatedUserId(processInstance.getStartUserId());
                taskService.addComment(firstTask.getId(), firstTask.getProcessInstanceId(), "自动提交表单");

                // 分配目标任务-受理人
                Map<String, Object> transientVariables = new HashMap<>(5);
                transientVariables.put(TransientVariableConst.ASSIGNEE, start.getAssignee());
                // 分配目标任务-候选人
                transientVariables.put(TransientVariableConst.CANDIDATE_USER_LIST, start.getCandidate().getUsers());
                for (String userId : start.getCandidate().getUsers()) {
                    taskService.addCandidateUser(firstTask.getId(), userId);
                }
                // 分配目标任务-候选组
                transientVariables.put(TransientVariableConst.CANDIDATE_GROUP_LIST, start.getCandidate().getGroups());
                for (String groupId : start.getCandidate().getGroups()) {
                    taskService.addCandidateGroup(firstTask.getId(), groupId);
                }

                // 分配目标任务-到期时间
                transientVariables.put(TransientVariableConst.DUE_DATE, start.getDueDate());

                // 分配目标任务-优先级
                transientVariables.put(TransientVariableConst.PRIORITY, start.getPriority());

                // 办理决策
                transientVariables.put(TransientVariableConst.AGREE, true);

                // 完成
                taskService.complete(firstTask.getId(), null, transientVariables);
            }
        }
        return processInstance;
    }

    /**
     * 分页列表（运行中的流程实例列表）
     */
    @Override
    public List<ProcessInstance> list(ProcessInstanceQuery query) {
        org.activiti.engine.runtime.ProcessInstanceQuery processInstanceQuery = runtimeService.createProcessInstanceQuery()
                .orderByTenantId().asc()
                .orderByProcessDefinitionKey().desc();
        if (StringUtils.isNotBlank(query.getTenantId())) {
            processInstanceQuery.processInstanceTenantId(query.getTenantId());
        }
        if (StringUtils.isNotBlank(query.getCategory())) {
            processInstanceQuery.processDefinitionCategory(query.getCategory());
        }
        if (query.isActive()) {
            processInstanceQuery.active();
        }
        if (StringUtils.isNotBlank(query.getProcessInstanceId())) {
            processInstanceQuery.processInstanceId(query.getProcessInstanceId());
        }
        if (StringUtils.isNotBlank(query.getName())) {
            processInstanceQuery.processInstanceNameLike("%" + query.getName() + "%");
        }
        if (StringUtils.isNotBlank(query.getStartedUserId())) {
            processInstanceQuery.startedBy(query.getStartedUserId());
        }
        if (StringUtils.isNotBlank(query.getInvolvedUserId())) {
            processInstanceQuery.involvedUser(query.getInvolvedUserId());
        }
        if (query.getStartDate() != null) {
            processInstanceQuery.startedBefore(query.getStartDate());
        }
        if (query.getEndDate() != null) {
            processInstanceQuery.startedAfter(query.getEndDate());
        }
        return processInstanceQuery.list();
    }

    /**
     * 获取流程实例
     */
    @Override
    public ProcessInstance get(String processInstanceId) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();
        if (processInstance == null || StringUtils.isBlank(processInstance.getProcessDefinitionId())) {
            throw new ActivitiObjectNotFoundException("Cannot find process instance with processInstanceId "
                    + processInstanceId, ProcessInstance.class);
        }
        return processInstance;
    }

    /**
     * 获取预览图(流程实例运行情况概览图)
     */
    @Override
    public InputStream getImage(String processInstanceId) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();
        if (processInstance == null || StringUtils.isBlank(processInstance.getProcessDefinitionId())) {
            throw new ActivitiObjectNotFoundException("Cannot find process instance with processInstanceId "
                    + processInstanceId, ProcessInstance.class);
        }
        String processDefinitionId = processInstance.getProcessDefinitionId();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        if (bpmnModel == null) {
            throw new ActivitiObjectNotFoundException("Cannot find bpmn model with processDefinitionId "
                    + processDefinitionId, BpmnModel.class);
        }
        String activityFontName = workflowProperties.getFontName();
        String labelFontName = workflowProperties.getFontName();
        String annotationFontName = workflowProperties.getFontName();
        // 已执行的活动节点
        List<String> highLightedActivities = new ArrayList<>();
        // 已执行的连接线
        List<String> highLightedFlows = new ArrayList<>();

        // 拿到已流转过的线路与节点记录
        List<HistoricActivityInstance> activityInstances = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByHistoricActivityInstanceStartTime().asc()
                .list();
        if (!CollectionUtils.isEmpty(activityInstances)) {
            List<SequenceFlow> sequenceFlows = bpmnModel.getMainProcess().findFlowElementsOfType(SequenceFlow.class);
            for (HistoricActivityInstance activityInstance : activityInstances) {
                // 添加高亮节点
                highLightedActivities.add(activityInstance.getActivityId());
                // 添加高亮连接线
                FlowElement flowElement = bpmnModel.getMainProcess().getFlowElement(activityInstance.getActivityId(), true);
                for (SequenceFlow sequenceFlow : sequenceFlows) {
                    if (sequenceFlow.getTargetFlowElement().getId().equals(flowElement.getId())) {
                        highLightedFlows.add(sequenceFlow.getId());
                    }
                }
            }
        }
        return new DefaultProcessDiagramGenerator().generateDiagram(bpmnModel, highLightedActivities, highLightedFlows
                , activityFontName, labelFontName, annotationFontName);
    }

    /**
     * 终止
     */
    @Override
    public void cancel(String processInstanceId, String deleteReason) {
        runtimeService.deleteProcessInstance(processInstanceId, deleteReason);
    }

    /**
     * 激活
     */
    @Override
    public void activate(String processInstanceId) {
        runtimeService.activateProcessInstanceById(processInstanceId);
    }

    /**
     * 挂起
     */
    @Override
    public void suspend(String processInstanceId) {
        runtimeService.suspendProcessInstanceById(processInstanceId);
    }


}
